/* eslint-disable regexp/no-obscure-range */
/* eslint-disable regexp/no-unused-capturing-group */
/**
 * 判断是否 十六进制颜色值.输入形式可为 `#fff000` `#f00`
 * @param color - 十六进制颜色值
 * @returns Boolean
 */
export function isHexColor(color: string) {
  const reg = /^#([0-9a-fA-F]{3}|[0-9A-f]{6})$/
  return reg.test(color)
}

/**
 * RGB 颜色值转换为 十六进制颜色值.r, g, 和 b 需要在 [0, 255] 范围内.
 * @returns  类似`#ff00ff`
 * @param r
 * @param g
 * @param b
 */
export function rgbToHex(r: number, g: number, b: number) {
  // tslint:disable-next-line:no-bitwise
  const hex = ((r << 16) | (g << 8) | b).toString(16)
  return `#${Array.from({ length: Math.abs(hex.length - 7) }).join('0')}${hex}`
}

/**
 * Transform a HEX color to its RGB representation
 * @param hex - The color to transform
 * @returns The RGB representation of the passed color
 */
export function hexToRGB(hex: string, opacity?: number) {
  let sHex = hex.toLowerCase()
  if (isHexColor(hex)) {
    if (sHex.length === 4) {
      let sColorNew = '#'
      for (let i = 1; i < 4; i += 1)
        sColorNew += sHex.slice(i, i + 1).concat(sHex.slice(i, i + 1))

      sHex = sColorNew
    }
    const sColorChange: number[] = []
    for (let i = 1; i < 7; i += 2)
      sColorChange.push(Number.parseInt(`0x${sHex.slice(i, i + 2)}`))

    return opacity
      ? `RGBA(${sColorChange.join(',')},${opacity})`
      : `RGB(${sColorChange.join(',')})`
  }
  return sHex
}

export function colorIsDark(color: string) {
  if (!isHexColor(color))
    return
  const [r, g, b] = hexToRGB(color)
    .replace(/(?:\(|\)|rgb|RGB)*/g, '')
    .split(',')
    .map(item => Number(item))
  return r * 0.299 + g * 0.578 + b * 0.114 < 192
}

/**
 * Darkens a HEX color given the passed percentage
 * @param color - The color to process
 * @param amount - The amount to change the color by
 * @returns The HEX representation of the processed color
 */
export function darken(color: string, amount: number) {
  color = color.includes('#') ? color.slice(1, color.length) : color
  amount = Math.trunc((255 * amount) / 100)
  return `#${subtractLight(color.slice(0, 2), amount)}${subtractLight(
    color.slice(2, 4),
    amount,
  )}${subtractLight(color.slice(4, 6), amount)}`
}

/**
 * Lightens a 6 char HEX color according to the passed percentage
 * @param color - The color to change
 * @param amount - The amount to change the color by
 * @returns The processed color represented as HEX
 */
export function lighten(color: string, amount: number) {
  color = color.includes('#') ? color.slice(1, color.length) : color
  amount = Math.trunc((255 * amount) / 100)
  return `#${addLight(color.slice(0, 2), amount)}${addLight(
    color.slice(2, 4),
    amount,
  )}${addLight(color.slice(4, 6), amount)}`
}

/* Suma el porcentaje indicado a un color (RR, GG o BB) hexadecimal para aclararlo */
/**
 * Sums the passed percentage to the R, G or B of a HEX color
 * @param color - The color to change
 * @param amount - The amount to change the color by
 * @returns The processed part of the color
 */
function addLight(color: string, amount: number) {
  const cc = Number.parseInt(color, 16) + amount
  const c = cc > 255 ? 255 : cc
  return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
}

/**
 * Calculates luminance of an rgb color
 * @param r - red
 * @param g - green
 * @param b - blue
 */
function luminanace(r: number, g: number, b: number) {
  const a = [r, g, b].map((v) => {
    v /= 255
    return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4
  })
  return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722
}

/**
 * Calculates contrast between two rgb colors
 * @param rgb1 - rgb color 1
 * @param rgb2 - rgb color 2
 */
function contrast(rgb1: string[], rgb2: number[]) {
  return (
    (luminanace(
      Math.trunc(Number(rgb1[0])),
      Math.trunc(Number(rgb1[1])),
      Math.trunc(Number(rgb1[2])),
    )
    + 0.05)
    / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05)
  )
}

/**
 * Determines what the best text color is (black or white) based con the contrast with the background
 * @param hexColor - Last selected color by the user
 */
export function calculateBestTextColor(hexColor: string) {
  const rgbColor = hexToRGB(hexColor.slice(1))
  const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0])

  return contrastWithBlack >= 12 ? '#000000' : '#FFFFFF'
}

/**
 * Subtracts the indicated percentage to the R, G or B of a HEX color
 * @param color - The color to change
 * @param amount - The amount to change the color by
 * @returns The processed part of the color
 */
function subtractLight(color: string, amount: number) {
  const cc = Number.parseInt(color, 16) - amount
  const c = cc < 0 ? 0 : cc
  return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
}

/**
 * @param color - 基准色
 * @param amount - 数值越小颜色越浅，反之颜色越深
 */
export function changeColorByBaseColor(color: string, amount: number): string {
  return `#${color
    .replace(/^#/, '')
    .replace(/../g, (color: string) =>
      `0${Math.min(
        255,
        Math.max(0, Number.parseInt(color, 16) - amount),
      ).toString(16)}`.slice(-2))}`
}
/**
 * Mixes two colors.
 *
 * @param color1 - The first color, should be a 6-digit hexadecimal color code starting with `#`.
 * @param color2 - The second color, should be a 6-digit hexadecimal color code starting with `#`.
 * @param weight - The weight of color1 in the mix, should be a number between 0 and 1, where 0 represents 100% of color2, and 1 represents 100% of color1.
 * @returns The mixed color, a 6-digit hexadecimal color code starting with `#`.
 */
export function mix(
  color1: string,
  color2: string,
  weight: number = 0.5,
): string {
  let color = '#'
  for (let i = 0; i <= 2; i++) {
    const c1 = Number.parseInt(color1.substring(1 + i * 2, 3 + i * 2), 16)
    const c2 = Number.parseInt(color2.substring(1 + i * 2, 3 + i * 2), 16)
    const c = Math.round(c1 * weight + c2 * (1 - weight))
    color += c.toString(16).padStart(2, '0')
  }
  return color
}
